#include <boost/lexical_cast.hpp>

#include "detect_cliques.h"
#include "detect_nodes.h"
#include "sequence_corpus_reader.h"

void CliqueDetector::Event::write(std::ostream &out)
{
	return crf_write(out);
}

void CliqueDetector::Event::crf_write(std::ostream &out)
{
	out << node_chart->order << ' ' <<  length << '\n';
	// print the nodes
	for (size_t i=0; i<node_chart->features.size(); ++i)
	{
		for (size_t s=0; s<node_chart->features[i].size(); ++s)
		{
			const NodeDetector::RowFeaturesPtr &rf 
				= node_chart->features[i][s];
			out << rf->nodes.size() <<'\n';
			for (size_t j=0; j<rf->nodes.size(); ++j)
			{
				out << rf->nodes[j]->gold;
				for (NodeDetector::FeatureSet::const_iterator it
					 = rf->nodes[j]->features.begin(); 
					 it != rf->nodes[j]->features.end(); ++it)
					if (it->second > 0)
						out << ' ' << it->first << ':' << it->second;
				out << '\n';
			}
		}
	}
	// print the cliques
	for (size_t i=0; i<cliques.size(); ++i)
		for (size_t s=0; s<cliques[i].size(); ++s)
		{
			// doesn't work for semi-CRF!
			out << node_chart->features[i][0]->nodes.size() 
				<< " " << node_chart->features[i+1][0]->nodes.size() << '\n';
			for (size_t j=0; j<cliques[i][s]->labellings.size(); ++j)
			{
				for (NodeDetector::FeatureSet::const_iterator it
					 = cliques[i][s]->labellings[j]->begin(); 
					 it != cliques[i][s]->labellings[j]->end(); 
					 ++it)
					if (it->second > 0)
						out << ' ' << it->first << ':' << it->second;
				out << '\n';
			}
		}
}

CliqueDetector::CliqueDetector(boost::shared_ptr<Parameters> p)
: mySentenceNum(0), myParams(p), myNodeDetector(new NodeDetector(p,true))
{
}

CliqueDetector::~CliqueDetector()
{
}

CliqueDetector::EventPtr CliqueDetector::next()
{
	return crf_next();
}

void CliqueDetector::skip()
{
	myNodeDetector->next();
}

CliqueDetector::EventPtr CliqueDetector::crf_next()
{
	mySentenceNum++;
	myLastNodeFeatures = myNodeDetector->next();
	if (myLastNodeFeatures->features.empty()) return EventPtr();

	EventPtr e(new Event(myLastNodeFeatures));
	FeatureMapPtr feature_map = myNodeDetector->feature_map();
	int detected_feature = -1;
	size_t prev_labels=0;
	std::string prev_word="";
	for (size_t i=0; i<myLastNodeFeatures->features.size(); ++i)
	{
		for (size_t s=0; s<myLastNodeFeatures->features[i].size(); ++s)
		{
			CliquePtr clique(new Clique);
			const NodeDetector::RowFeaturesPtr &rf=myLastNodeFeatures->features[i][s];
			size_t num_labels = rf->nodes.size();
			for (size_t j=0; j<rf->nodes.size(); ++j)
			{
//				std::string nf_str = boost::lexical_cast<std::string>(j);
				std::string nf_str = boost::lexical_cast<std::string>
					((int)feature_map->map_tag(rf->word,j));

				// FINAL NODES
				if (feature_map->frozen() || rf->nodes[j]->gold)
//				if(true)
				{
					if (i == myLastNodeFeatures->features.size()-1)
					{
						std::string end_str = boost::lexical_cast<std::string>
							(e->node_chart->end_label); 

						// add end clique feature to final nodes
						detected_feature = feature_map->add
							(std::string("CLIQUE=")+nf_str+"_"+end_str);
						if (detected_feature >= 0)
							rf->nodes[j]->features.insert(
								std::pair<int,float>(detected_feature,1.0f));
					}
					// INITIAL NODES
					if (i-s==0)
					{
						std::string start_str = boost::lexical_cast<std::string>
							(e->node_chart->start_label); 

						// add start clique feature to any node segment that starts
						// from the start of the sequence
						detected_feature = feature_map->add
							(std::string("CLIQUE=")+start_str+"_"+nf_str);
						if (detected_feature >= 0)
							rf->nodes[j]->features.insert(
								std::pair<int,float>(detected_feature,1.0f));
					}
				}
				// CLIQUES
				if (i-s != 0)
				{
					assert(prev_labels);
					assert(prev_labels==feature_map->word_tags(prev_word) 
						|| !feature_map->word_tags(prev_word));
					for (size_t l=0; l<prev_labels; ++l)
					{
						NodeDetector::FeatureSetPtr clique_features(new NodeDetector::FeatureSet);
//						std::string prev_str = boost::lexical_cast<std::string>(l); 
						std::string prev_str = boost::lexical_cast<std::string>
							((int)feature_map->map_tag(prev_word,l));
						// doesn't work for semi-CRF
						if (feature_map->frozen() || (rf->nodes[j]->gold
							&& myLastNodeFeatures->features[i-1][0]->nodes[l]->gold))
						{
							// simple clique labelling feature
							detected_feature = feature_map->add
								(std::string("CLIQUE=")+prev_str+"_"+nf_str);
							if (detected_feature >= 0)
								clique_features->insert
									(std::pair<int,float>(detected_feature,1.0f));
						}
						clique->labellings.push_back(clique_features);
					}
				}
			}
			if (i-s!=0) e->cliques[i-1].push_back(clique);
			prev_word = myLastNodeFeatures->features[i][s]->word; //BROKEN FOR SEMI-CRF
			prev_labels = num_labels;
		}
	}
	return e;
}
